package com.free.bsf.core.util;

import com.free.bsf.core.base.BsfException;
import com.free.bsf.core.config.CoreProperties;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.Modifier;
import lombok.val;
import lombok.var;
import org.apache.commons.codec.digest.DigestUtils;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Classname DynamicCodeUtil
 * @Description 【警告】不同代码频繁使用，有内存溢出风险
 * @Date 2021/5/12 13:34
 * @Created by chejiangyi
 */
public class DynamicCodeUtil {
    private static Map<String,Object> cache=new ConcurrentHashMap<>();
    public static class DynamicContext extends HashMap<String,Object>{

    }

    private static String runTemplate(String code){
        return ("{{DynamicContextClass} context=$1;\n" +
                "{code}\n" +
                "return context;\n}").replace("{DynamicContextClass}",DynamicContext.class.getName()).replace("{code}",code);
    }

    public static DynamicContext run(String code,DynamicContext context){
        String md5=DigestUtils.md5Hex(code.getBytes());
        var instance = cache.get(md5);
        if(instance==null) {
            compile(md5, code);
            instance = cache.get(md5);
        }
        context = ReflectionUtils.tryCallMethod(instance,"run",new Object[]{context},null);
        if(context==null||!(context instanceof DynamicContext))
            throw new BsfException("调用失败");
        int maxCache = PropertyUtils.getPropertyCache("bsf.dynamicCode.cache.max",500);
        if(cache.size()>maxCache){
            LogUtils.error(DynamicCodeUtil.class, CoreProperties.Project,"[报警]动态编译缓存>"+maxCache+",有溢出风险!");
        }
        return context;
    }

    private static synchronized void compile(String md5,String code){
        try {
             val cls = cache.get(md5);
             if(cls!=null)
                 return;
            val cp = ClassPoolUtils.getInstance();
            val contextClass = cp.get(DynamicContext.class.getName());
            CtClass clazz = cp.makeClass("DynamicCode_" + UUID.randomUUID().toString().replace("-", ""));
            CtMethod ctMethod = new CtMethod(contextClass,
                    "run", new CtClass[]{contextClass}, clazz);
            ctMethod.setModifiers(Modifier.PUBLIC);
            ctMethod.setBody(runTemplate(code));
            clazz.addMethod(ctMethod);
            cache.put(md5,clazz.toClass().newInstance());
        }catch (Exception exp){
            throw new BsfException("编译失败",exp);
        }
    }

    public static void main(String[] args) {
        DynamicContext context = new DynamicContext();
        for(int i=0;i<600;i++){
            context.put("aaa",i);
            DynamicCodeUtil.run(" System.out.println(context.get(\"aaa\"));\n"+"//"+i+"\n",context);
        }
    }
}
